package com.iflytek.autoCompareTool.service;

import com.alibaba.fastjson.JSON;
import com.iflytek.autoCompareTool.common.Config;
import com.iflytek.autoCompareTool.common.Constants;
import com.iflytek.autoCompareTool.common.DBManager;
import com.iflytek.autoCompareTool.common.TaskManager;
import com.iflytek.autoCompareTool.model.InputInfo;
import com.iflytek.autoCompareTool.model.TestData;
import com.iflytek.autoCompareTool.model.TestSet;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;


/**
 * 监控器
 *
 * @author llchen12
 * @date 2018/5/31
 */
public class Monitor implements Runnable {

    private static Logger logger = LoggerFactory.getLogger(Monitor.class);

    private QueryRunner qr;


    private ExecutorService taskExecutor;

    public Monitor(){
        this.qr=DBManager.getInstance().getAiuiManagerQr();
        this.taskExecutor=Executor.getInstance().getProcessThreadPool();
    }


    @Override
    public void run() {

        logger.debug("monitor start...");

        LinkedList<InputInfo> inputInfoList = process();

        for (InputInfo input : inputInfoList) {
            Integer id = input.getId();
            if (TaskManager.containsTask(id)) {
                continue;
            }
            Map<String, List<TestSet>> testSetMap;
            //id<0,上传文件处理
            if (id < 0) {
                if (StringUtils.equals(input.getFileType(), Constants.CUSTOM_TEST)) {
                    testSetMap = TestData.parseInput(input);
                } else {
                    continue;
                }

                //id>0,其他测试集测试任务
            } else {
                testSetMap = TestData.parseInput(input);
            }

            //检测测试集,同时更新数据库中该测试任务测试集大小信息
            if (!checkTestSetMap(testSetMap, input)) {
                continue;
            }

            TestData testData = new TestData(input, testSetMap);

            if (logger.isDebugEnabled()) {
                logger.debug("testSetMap data：{}", JSON.toJSONString(testSetMap, true));
            }

            TaskManager.addTask(id, testData.size());

            taskExecutor.execute(new Processor(testData));
        }
    }


    /**
     * 从数据库中查找新任务
     *
     * @return 任务
     */
    private LinkedList<InputInfo> process() {

        String sql = "select id,fileType,filePath,baseEnv,cmpEnv,singleEnv,baseOther,cmpOther,answer,mail " +
                "from tool_testupload " +
                "where toolId=? and fileType=? and progress=?";

        String toolId = Config.TOOL_ID;
        Future<List<InputInfo>> testSetInputsFuture = taskExecutor.submit(() -> {
            List<InputInfo> res = null;
            try {
                res = qr.query(sql, new BeanListHandler<>(InputInfo.class), toolId, Constants.FILE_TYPE_TEST_SET, 0);

            } catch (Exception e) {
                logger.error("监控数据库异常", e);
            }
            return res;
        });

        Future<List<InputInfo>> customTestInputsFuture = taskExecutor.submit(() -> {
            List<InputInfo> res = null;
            try {
                res = qr.query(sql, new BeanListHandler<>(InputInfo.class), toolId, Constants.FILE_TYPE_CUSTOM_TEST, 0);

            } catch (Exception e) {
                logger.error("从数据库获取数据异常", e);
            }
            return res;
        });

        List<InputInfo> testSetInputs = null;
        List<InputInfo> customTestInputs = null;
        try {
            testSetInputs = testSetInputsFuture.get();
            customTestInputs = customTestInputsFuture.get();

        } catch (InterruptedException | ExecutionException e) {
            logger.error("异步获取数据异常", e);
        }

        LinkedList<InputInfo> res = new LinkedList<>();

        if (testSetInputs != null && testSetInputs.size() > 0) {
            res.addAll(testSetInputs);
        }

        //查看上传任务数据库，将上传任务的id变成负数，与测试集任务区分
        if (customTestInputs != null && customTestInputs.size() > 0) {
            for (InputInfo in : customTestInputs) {
                Integer id = in.getId();
                //转成负数
                id = ~id + 1;
                in.setId(id);
                res.add(in);
            }
        }
        return res;
    }


    /**
     * 检测该任务对应的测试集是否为空，并设置数据库。
     *
     * @param testSetMap 该任务的测试集
     * @param input      任务的输入数据
     * @return 空返回false，不为空返回true
     */
    private boolean checkTestSetMap(Map<String, List<TestSet>> testSetMap, InputInfo input) {

        Integer id = input.getId();
        boolean res;
        if (testSetMap == null) {
            updateProgress(Math.abs(id), 100, null);
            logger.warn("测试集生成失败！发送邮件通知{}", input.getMail());
            res = false;
        } else {
            //计算该id测试集大小
            int testSetSize = 0;
            for (List<TestSet> testSetList : testSetMap.values()) {
                testSetSize += testSetList.size();
            }

            if (testSetSize == 0) {
                //TODO:发送邮件
                logger.debug("测试集为空！发送邮件通知{}", input.getMail());
                res = false;
            } else {
                res = true;
            }

            //更新数据库
            updateProgress(Math.abs(id), null, testSetSize);
        }
        return res;
    }


    /**
     * 更新数据库的进度
     *
     * @param id          测试任务id 必填
     * @param progress    进度
     * @param testSetSize 测试集大小 可以为空
     */
    private void updateProgress(Integer id, Integer progress, Integer testSetSize) {

        if (progress != null || testSetSize != null) {
            Object[] args = null;
            String sql = "update tool_testupload set ";
            if (testSetSize == null) {
                sql += "progress=? ";
                args = new Object[]{progress, Math.abs(id)};
            }

            if (progress == null) {
                sql += "testsetSize=? ";
                args = new Object[]{testSetSize, Math.abs(id)};
            }

            if (progress != null && testSetSize != null) {
                sql += "progress=? ,testsetSize=? ";
                args = new Object[]{progress, testSetSize, Math.abs(id)};
            }

            sql += "where id=?";

            //失败重试2次
            for (int i = 0; i < 3; i++) {
                try {
                    int res = qr.update(sql, args);
                    if (res == 1) {
                        break;
                    }
                } catch (SQLException e) {
                    logger.error("第{}次更新 id->{} 任务进度失败", i, id, e);
                }
            }
        }
    }


}
